﻿#region Copyright
/* Product of DTL Software
 * Product Name : Support Memmory
 * Author : dinhtienlam.spkt@gmail.com
 * About: Developer of DTL Software
 * Contact : 0966 330 237
 */
#endregion

#region Using default
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Design;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms.Design;
using System.Windows.Forms; 
#endregion

namespace Lamdt.Controls
{
    [Designer(typeof(ControlDesigner))]
    public class LamdtDataGridView : DataGridView
    {
        #region Private variables
        private static Color DefaultColor = Color.FromArgb(163, 220, 230);
        private static Color HeaderColor = Color.FromArgb(17, 166, 206);
        private static Color MouseMoveColor = Color.FromArgb(223, 247, 254);
        private const string ColSelected = "ColSelected";
        private DataGridViewColumn dgvcSelect = null;
        private bool IsbdComplete = false;
        #endregion

        #region Public variables
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsBindDataComplete
        {
            get { return IsbdComplete; }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<DataGridViewRow> CheckedRows
        {
            get
            {
                List<DataGridViewRow> result = new List<DataGridViewRow>();
                if (CheckedColumnIndex >= 0 || this.Rows.Count > 0)
                {
                    foreach (DataGridViewRow r in this.Rows)
                    {
                        if (r.Cells[CheckedColumnIndex].Value != null &&
                            (bool)r.Cells[CheckedColumnIndex].Value)
                            result.Add(r);
                    }
                }
                return result;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool SetCheckAllClick
        {
            set
            {
                chkClick = value;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsCheckedGridView
        {
            get
            {
                return dgvcSelect != null;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int CheckedColumnIndex
        {
            get
            {
                if (dgvcSelect == null)
                    return -1;
                else
                    return dgvcSelect.Index;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public DataGridViewColumn IDColumn { set; get; }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public object dtlDataSource
        {
            get
            {
                return base.DataSource;
            }
            set
            {
                if (base.DataSource == value)
                    return;
                if (IDColumn == null)
                {
                    base.DataSource = value;
                    return;
                }
                List<object> SelectedIDList = new List<object>();
                if (this.IsCheckedGridView)
                {
                    foreach (DataGridViewRow dgvr in this.CheckedRows)
                    {
                        SelectedIDList.Add(this[IDColumn.Index, dgvr.Index].Value);
                    }
                }
                else
                {
                    foreach (DataGridViewRow dgvr in this.SelectedRows)
                    {
                        SelectedIDList.Add(this[IDColumn.Index, dgvr.Index].Value);
                    }
                }
                int currentViewRowIndex = this.FirstDisplayedScrollingRowIndex;
                int currentViewColumnIndex = this.FirstDisplayedScrollingColumnIndex;
                base.DataSource = value;
                if (this.Rows.Count <= 0)
                    return;
                if (currentViewRowIndex > 0)
                {
                    if (this.Rows.Count > currentViewRowIndex)
                        this.FirstDisplayedScrollingRowIndex = currentViewRowIndex;
                    else
                        this.FirstDisplayedScrollingRowIndex = this.Rows.Count - 1;
                }
                if (currentViewColumnIndex > 0)
                {
                    if (this.Columns.Count > currentViewColumnIndex)
                        this.FirstDisplayedScrollingColumnIndex = currentViewColumnIndex;
                    else
                        this.FirstDisplayedScrollingColumnIndex = this.Columns.Count - 1;
                }
                foreach (DataGridViewRow dgvr in this.Rows)
                {
                    if (SelectedIDList.Contains(this[IDColumn.Index, dgvr.Index].Value))
                    {
                        if (this.IsCheckedGridView)
                            this.SetCheckedRow(dgvr.Index);
                        else
                            dgvr.Selected = true;
                    }
                    else
                        dgvr.Selected = false;
                }
                IsbdComplete = true;
            }
        }
        #endregion

        #region Constructor
        public LamdtDataGridView()
        {
            InitializeComponent();
            SetPropertyAndEvent();
        }

        private void InitializeComponent()
        {
            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            this.SuspendLayout();
            // 
            // LamdtDataGridView
            // 
            this.AllowUserToAddRows = false;
            this.AllowUserToDeleteRows = false;
            this.AllowUserToOrderColumns = true;
            this.AutoGenerateColumns = false;
            this.BackgroundColor = System.Drawing.Color.White;
            dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
            dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
            dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            dataGridViewCellStyle1.ForeColor = System.Drawing.Color.White;
            dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
            dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
            dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
            this.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
            this.EnableHeadersVisualStyles = false;
            this.ReadOnly = true;
            this.RowHeadersVisible = false;
            this.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
            this.ColumnHeadersHeightChanged += new System.EventHandler(this.LamdtDataGridView_ColumnHeadersHeightChanged);
            this.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.LamdtDataGridView_CellFormatting);
            this.CellMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.LamdtDataGridView_CellMouseClick);
            this.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.LamdtDataGridView_CellMouseEnter);
            this.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.LamdtDataGridView_CellMouseLeave);
            this.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.LamdtDataGridView_CellValueChanged);
            this.ColumnNameChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.LamdtDataGridView_ColumnNameChanged);
            this.ColumnWidthChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.LamdtDataGridView_ColumnWidthChanged);
            this.RowsAdded += new System.Windows.Forms.DataGridViewRowsAddedEventHandler(this.LamdtDataGridView_RowsAdded);
            this.SelectionChanged += new System.EventHandler(this.LamdtDataGridView_SelectionChanged);
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.LamdtDataGridView_Paint);
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);
        }
        #endregion

        #region Private Functions
        private void SetPropertyAndEvent()
        {
            DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
            dataGridViewCellStyle1.SelectionBackColor = DefaultColor;
            dataGridViewCellStyle1.ForeColor = Color.Black;
            this.RowsDefaultCellStyle = dataGridViewCellStyle1;
            this.BorderStyle = BorderStyle.FixedSingle;
            this.ColumnHeadersDefaultCellStyle.BackColor = HeaderColor;
            this.GridColor = HeaderColor;
        }

        private void LamdtDataGridView_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
                if (this.Rows[e.RowIndex].DefaultCellStyle.BackColor != MouseMoveColor &&
                    this.Rows[e.RowIndex].DefaultCellStyle.BackColor != DefaultColor)
                {
                    this.Rows[e.RowIndex].DefaultCellStyle.BackColor = MouseMoveColor;
                    if (IDColumn != null)
                    {
                        List<DataGridViewRow> lst =
                         this.Rows.Cast<DataGridViewRow>().Where(
                             c => c.Cells[IDColumn.Name].Value.ToString().Equals(
                                 this.Rows[e.RowIndex].Cells[IDColumn.Name].Value.ToString())).ToList();
                        if (lst != null)
                            foreach (DataGridViewRow r in lst)
                                r.DefaultCellStyle.BackColor = MouseMoveColor;
                    }
                }
        }

        private void LamdtDataGridView_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
                if (this.Rows[e.RowIndex].DefaultCellStyle.BackColor == MouseMoveColor
                    && !this.Rows[e.RowIndex].Selected
                    )
                {
                    this.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.White;
                    if (IDColumn != null)
                    {
                        List<DataGridViewRow> lst =
                         this.Rows.Cast<DataGridViewRow>().Where(
                             c => c.Cells[IDColumn.Name].Value.ToString().Equals(
                                 this.Rows[e.RowIndex].Cells[IDColumn.Name].Value.ToString())).ToList();
                        if (lst != null)
                            foreach (DataGridViewRow r in lst)
                                r.DefaultCellStyle.BackColor = Color.White;
                    }
                }
        }

        private void LamdtDataGridView_SelectionChanged(object sender, EventArgs e)
        {
            if (CheckedColumnIndex >= 0)
                if (this.CurrentRow.Cells[CheckedColumnIndex].Value == null ||
                    (this.CurrentRow.Cells[CheckedColumnIndex].Value is bool &&
                    (bool)this.CurrentRow.Cells[CheckedColumnIndex].Value == false))
                    this.CurrentRow.Selected = false;
            foreach (DataGridViewRow r in this.Rows)
            {
                if (CheckedColumnIndex < 0)
                {
                    if (!r.Selected)
                        r.DefaultCellStyle.BackColor = Color.White;
                }
                else
                {
                    if (r.Cells[CheckedColumnIndex].Value == null ||
                        (bool)r.Cells[CheckedColumnIndex].Value == false)
                    {
                        r.Selected = false;
                    }
                    else
                    {
                        if (r.DefaultCellStyle.BackColor != DefaultColor)
                            r.DefaultCellStyle.BackColor = DefaultColor;
                    }
                }
            }
        }

        public void LamdtDataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            chkClick = false;
            if (CheckedColumnIndex >= 0 && e.RowIndex >= 0 && e.ColumnIndex >= 0)
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    if (this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value == null)
                        this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value = false;
                    this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value = !(bool)this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value;
                }
        }

        private void LamdtDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
                if (CheckedColumnIndex >= 0)
                    if (e.ColumnIndex == CheckedColumnIndex)
                    {
                        if (!(this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value is bool))
                            this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value = false;
                        if (this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value == null ||
                            (bool)this.Rows[e.RowIndex].Cells[CheckedColumnIndex].Value == false)
                        {
                            this.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.White;
                            this.Rows[e.RowIndex].Selected = false;
                        }
                        else
                        {
                            this.Rows[e.RowIndex].DefaultCellStyle.BackColor = DefaultColor;
                        }

                        if (Controls.Contains(chkBox) && !chkClick)
                        {
                            bool IsAllRecordsSeleted = true;
                            foreach (DataGridViewRow r in this.Rows)
                            {
                                if (r.Cells[CheckedColumnIndex].Value == null ||
                                    (r.Cells[CheckedColumnIndex].Value is bool &&
                                        (bool)r.Cells[CheckedColumnIndex].Value == false))
                                    IsAllRecordsSeleted = false;
                            }
                            chkBox.CheckedChanged -= chkBox_CheckedChanged;
                            chkBox.Checked = IsAllRecordsSeleted;
                            chkBox.CheckedChanged += chkBox_CheckedChanged;
                        }
                    }
        }

        private void LamdtDataGridView_ColumnNameChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (CheckedColumnIndex >= 0)
                return;
            if (e.Column is DataGridViewCheckBoxColumn && e.Column.Name == ColSelected)
            {
                e.Column.HeaderText = string.Empty;
                dgvcSelect = e.Column;
                AddCheckBoxToHeader();
            }
        }

        private void LamdtDataGridView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            (Controls[1] as ScrollBar).Maximum = RowCount * RowTemplate.Height;
        }

        private void LamdtDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            e.ThrowException = false;
        }

        /// <summary>
        /// To format cell, ex: Datetime, number ...
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LamdtDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            object value = this[e.ColumnIndex, e.RowIndex].Value;
            if (value != null &&
                (value is DateTime ||
                value is int ||
                value is decimal ||
                value is long ||
                value is float ||
                value is double))
            {
                e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                this.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
            }
            if (value != null &&
                value is DateTime)
            {
                e.Value = ((DateTime)value).ToString("yyyy/MM/dd");
            }
        }

        #region Add Checkbox to column header.
        private CheckBox chkBox = new CheckBox();
        private bool chkClick = false;
        private const string checkAllName = "chkAllRecords";
        Rectangle rect = new Rectangle();
        private void RePaint()
        {
            rect = this.GetCellDisplayRectangle(CheckedColumnIndex, -1, true);
            if (rect.Width == 0 && rect.Height == 0)
            {
                chkBox.Visible = false;
                return;
            }
            chkBox.Visible = true;
            int XLocation = 0;
            foreach (DataGridViewColumn dgvc in this.Columns)
            {
                if (dgvc.DisplayIndex != dgvcSelect.DisplayIndex - 1)
                    continue;
                if (this.GetCellDisplayRectangle(dgvc.Index, -1, true).Width > 0)
                {
                    XLocation = rect.X + dgvcSelect.Width / 2 - 7;
                    break;
                }
            }
            if (XLocation <= 0)
            {
                XLocation = rect.X + rect.Width - dgvcSelect.Width / 2 - 7;
            }
            int YLocation = this.ColumnHeadersHeight / 2 - 4;
            chkBox.Location = new Point(XLocation, YLocation);
        }

        private void AddCheckBoxToHeader()
        {
            chkBox.Size = new System.Drawing.Size(12, 12);
            chkBox.CheckedChanged += new EventHandler(chkBox_CheckedChanged);
            chkBox.Name = checkAllName;
            this.Controls.Add(chkBox);
        }

        private void chkBox_CheckedChanged(object sender, EventArgs e)
        {
            chkClick = true;
            foreach (DataGridViewRow row in this.Rows)
            {
                row.Cells[CheckedColumnIndex].Value = chkBox.Checked;
            }
        }

        private void LamdtDataGridView_Paint(object sender, PaintEventArgs e)
        {
            if (CheckedColumnIndex >= 0)
                RePaint();
        }

        private void LamdtDataGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (CheckedColumnIndex < 0)
                return;
            if (e.Column == dgvcSelect)
                if (e.Column.Width > 100)
                    e.Column.Width = 100;
                else if (e.Column.Width < 20)
                    e.Column.Width = 20;


        }

        private void LamdtDataGridView_ColumnHeadersHeightChanged(object sender, EventArgs e)
        {
            if (this.ColumnHeadersHeight > 100)
                this.ColumnHeadersHeight = 100;
            else if (this.ColumnHeadersHeight < 20)
                this.ColumnHeadersHeight = 20;
        }
        #endregion

        #region Merge Rows, Cells of DataGridView
        private void MergeColumnCellEx(int colIndex, int rowIndex, int numberRows)
        {
            var cell = (DataGridViewTextBoxCellEx)this[colIndex, rowIndex];
            cell.ColumnSpan = 1;
            cell.RowSpan = numberRows;
        }

        private void MergeColumnCellEx(string colName, int rowIndex, int numberRows)
        {
            var cell = (DataGridViewTextBoxCellEx)this[colName, rowIndex];
            cell.ColumnSpan = 1;
            cell.RowSpan = numberRows;
        }
        #endregion

        #endregion

        #region Public methods
        public void SetCheckedRow(int rowIndex)
        {
            if (dgvcSelect == null)
                throw new Exception("There is no checked column");
            else
            {
                this[CheckedColumnIndex, rowIndex].Value = true;
            }
        }
        /// <summary>
        /// Merge rows of DataGridView follow column names.
        /// </summary>
        /// <param name="colIDName">Name of ID column</param>
        /// <param name="otherColumnNames">Name of other columns need to marge.</param>
        public void MergeRows(string colIDName, List<string> otherColumnNames)
        {
            if (this.Rows.Count < 2) return;
            int startIndex = 0;
            for (int i = 1; i < this.Rows.Count; i++)
            {
                if (
                    this.Rows[i].Cells[colIDName].FormattedValue.ToString()
                    != this.Rows[startIndex].Cells[colIDName].FormattedValue.ToString() ||
                    i == this.Rows.Count - 1
                    )
                {
                    if ((i - startIndex) == (this.Rows.Count - 1))
                    {
                        MergeColumnCellEx(colIDName, startIndex, i - startIndex);
                        foreach (string OtherColumn in otherColumnNames)
                        {
                            MergeColumnCellEx(OtherColumn, startIndex, i - startIndex);
                        }
                    }
                    if (i == this.Rows.Count - 1)
                    {
                        if (this.Rows[i].Cells[colIDName].FormattedValue.ToString() ==
                            this.Rows[startIndex].Cells[colIDName].FormattedValue.ToString())
                        {
                            MergeColumnCellEx(colIDName, startIndex, i - startIndex + 1);
                            foreach (string OtherColumn in otherColumnNames)
                            {
                                MergeColumnCellEx(OtherColumn, startIndex, i - startIndex + 1);
                            }
                        }
                        return;
                    }
                    if ((i - startIndex) >= 2 && i != this.Rows.Count - 1)
                    {
                        MergeColumnCellEx(colIDName, startIndex, i - startIndex);
                        foreach (string OtherColumn in otherColumnNames)
                        {
                            MergeColumnCellEx(OtherColumn, startIndex, i - startIndex);
                        }
                    }
                    startIndex = i;
                }
            }
        }
        /// <summary>
        ///  Merge rows of DataGridView follow column names.
        /// </summary>
        /// <param name="colIDIndex">Index of ID column</param>
        /// <param name="OtherColumnNames">Name of other columns need to marge.</param>
        public void MergeRows(int colIDIndex, List<string> OtherColumnNames)
        {
            string ColumnName = this.Columns[colIDIndex].Name;
            MergeRows(ColumnName, OtherColumnNames);
        }
        /// <summary>
        /// Align midle right for columns is Numberic or DateTime.
        /// </summary>
        /// <param name="colNames">Column Names</param>
        public void SetNumberOrDateField(List<string> colNames)
        {
            foreach (string colName in colNames)
            {
                this.Columns[colName].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                this.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
            }
        }
        #endregion
    }
}
